/*
 * ads_mgr.cpp
 *
 *  Created on: Dec 21, 2012
 *      Author: wangmu
 */
#include "ads_mgr.h"
#include <sstream>
#include <stdlib.h>
#include <syslog.h>
#include "../../../src/XMLInterface/xml_utils.h"


#define XML_END_TAG 	"</Eadp>"
#define XML_END_TAG_LEN	7
unsigned int GetTickCount()
{
	struct timeval tv;
	if(gettimeofday(&tv,NULL) != 0)
		return 0;
	return (tv.tv_sec*1000) + (tv.tv_usec/1000);
}



/**
 * used for initial the assist data
 * Every CAdsUser should copy this to it's own data space for thread safe
 * don't change the default value
 */
service_item_enable default_gps_service_list[MAX_GPS_SERVICE_NUMBER] = {
		{ADS_SERVICE_ALMANAC,false},
		{ADS_SERVICE_UTCModel,false},
		{ADS_SERVICE_IonosphericModel,false},
		{ADS_SERVICE_NavigationModel,false},
		{ADS_SERVICE_DGPSCorrections,false},
		{ADS_SERVICE_ReferenceTime,false},
		{ADS_SERVICE_RealTimeIntegrity,false}
};



/**
 * data_type_convert
 * data type convert to ads service type
 * (string to int)
 */
ads_service_type data_type_convert_to_int(const string &type_str)
{
	if(type_str == "Period")
		return ADS_SERVICE_PERIOD;
	if(type_str == "Once")
		return ADS_SERVICE_ONCE;
	if(type_str == "Onchange")
		return ADS_SERVICE_ONCHANGED;
	if(type_str == "Stop")
		return ADS_SERVICE_STOP;

	return ADS_SERVICE_UNSUPPORT;
}
/**
 *
 */
void data_type_convert_to_str(ads_service_type s_type, string& out)
{
	if(s_type == ADS_SERVICE_PERIOD)
		out = "Period";
	else if(s_type == ADS_SERVICE_ONCE)
		out = "Once";
	else if(s_type == ADS_SERVICE_ONCHANGED)
		out = "Onchange";
	else if(s_type == ADS_SERVICE_STOP)
		out = "Stop";
	else
		out = "Unsupport";
}
/**
 * service list used for send assist data with enable flag
 */

/**
 *
 *CAdsUser::CAdsUser
 */
CAdsUser::CAdsUser(const IUserObserver& ob)
:m_fd(-1),m_state(ADS_USER_INIT),m_seq_number(1),m_req_number(0),m_observer(ob)
{
	Sem_init(&m_act_lck, 0, 1);      /* Binary semaphore for locking, every user will have one lock */
	clear_state();
}

/**
 * CAdsUser::~CAdsUser()
 */
CAdsUser::~CAdsUser()
{
	clear_state();
}
/*
 * send_xml to socket
 *
 */
int CAdsUser::send_xml_data(const string& str)
{
	int ret = 0;
	if(m_fd > 0)
	{
		ret = Rio_writen(m_fd,(void*) str.c_str(),str.size());         //line:netp:servestatic:write
		m_sent_bytes+=str.size();
		return ret;
	}else return -1;
}
int CAdsUser::send_file(string filename)

{
	struct stat sbuf;
	char *srcp;
	if (stat(filename.c_str(), &sbuf) < 0) {                     //line:netp:doit:beginnotfound
		printf("Forbidden Tiny couldn't read the file");
		return 0;
	}                                                    //line:netp:doit:endnotfound

	if (!(S_ISREG(sbuf.st_mode)) || !(S_IRUSR & sbuf.st_mode)) { //line:netp:doit:readable
		printf("Forbidden Tiny couldn't read the file");
		return 0;
	}
	syslog(LOG_INFO,"send file %s, file size %l\n",filename.c_str(), sbuf.st_size);
	int srcfd;
	/* Send response body to client */
	srcfd = Open(filename.c_str(), O_RDONLY, 0);    //line:netp:servestatic:open
	srcp = (char*) Mmap(0, sbuf.st_size, PROT_READ, MAP_PRIVATE, srcfd, 0);//line:netp:servestatic:mmap
	Close(srcfd);                           //line:netp:servestatic:close
	int ret = Rio_writen(m_fd, srcp, sbuf.st_size);         //line:netp:servestatic:write
	Munmap(srcp, sbuf.st_size);                 //line:netp:servestatic:munmap
	return ret;

}

/**
 * action_on_unscribe_or_data_req
 *
 * 1) read xml
 * 2) use CXml_utils(parse unscribe (return -1 let caller close socket )or data_req(update config_list))
 * 3) do action
 */

int CAdsUser::action_on_unscribe_or_data_req(const string &xmlrecvd)
{
	syslog(LOG_INFO, "%s on fd %d\n", __PRETTY_FUNCTION__,m_fd);
	// parser xml subscribe
	unsub_subscribe_request sub_req;
	sub_req.reqSeqNum = "0";
	//try parser xml with unsubscribe

	CXml_utils::do_parser_xml_un_subscribe_req(xmlrecvd, sub_req);
	if(sub_req.user_id.size() && sub_req.user_pwd.size() && sub_req.action == ADS_UN_SUBSCRIBE_STR)
	{
		syslog(LOG_INFO, "%s: fd %d request un-subscribe\n", __PRETTY_FUNCTION__,m_fd);
		//un-subscribe confirmed
		//return -1, let outside do the close socket.
		return -1;
	}else
	{
		// parser xml data req
		data_request_gps req_gps;
		req_gps.ReqSeqNum = "0";
		//if xml request not "unscribe_req" then try data request
		CXml_utils::do_parser_xml_data_request_gps(xmlrecvd,req_gps);

		if(req_gps.configList.size() && req_gps.data_request_type.size())
		{
			syslog(LOG_INFO, "%s: fd %d have data request\n", __PRETTY_FUNCTION__,m_fd);
			return do_action_on_data_request(req_gps);
		}
	}
	return -1;
}

/**
 * CAdsUser::read_socket(string &xmlbuffer)
 * success: return > recv size
 * failed: -1 , if failed, close (fd);
 */
int CAdsUser::read_socket(string &xmlbuffer)
{
	ssize_t n = 0;
	ssize_t nread = 0;
	char buf[BUFSIZ];
	while ((nread = read(m_fd, buf, BUFSIZ-1)) > 0) {
		n += nread;
		buf[nread] = 0;
		xmlbuffer+=buf;
		memset(buf,0,sizeof(buf));
	}
	if (nread == -1 && errno != EAGAIN) {
		perror("read error");
		return -1;
	}else
		if(nread == 0)
		{
			perror("remote closed socket");
			return -1;
		}
	return xmlbuffer.size();
}
/**
 * main do ads action
 * if some error happen(read/write socket, parser xml, check parameter)
 * 	  will close socket, clear state, save state to old_list for future use
 */
int CAdsUser::do_ads_action()
{

	P(&m_act_lck);
	unsigned int start_tick = GetTickCount();
	int ret = 0;
	syslog(LOG_INFO,"user [%d] at state %d\n",m_fd,m_state);

	if(m_fd < 0 || m_state < ADS_USER_CONNECTED)
	{
		m_total_tick += GetTickCount() -start_tick;
		V(&m_act_lck);
		return -1;
	}
	string xmlstr_recv; // will be used for parser and create res
	if(-1 == read_socket(xmlstr_recv))
	{

		clear_state();
		V(&m_act_lck);
		return -1;
	}

	//take care the whole xml can't recv in one time
	m_recv += xmlstr_recv;
	while(1)
	{
		size_t pos = 0;
		if((pos = m_recv.find(XML_END_TAG)) == string::npos )
		{// not found the XML_END_TAG, continue
			m_total_tick += GetTickCount() - start_tick;
			V(&m_act_lck);
			return 0;
		}else{
			size_t spos = pos+XML_END_TAG_LEN;
			xmlstr_recv = m_recv.substr(0,spos);//
			string leftstr = m_recv.substr(spos);
			if(spos == m_recv.size())
				m_recv.clear();
			else
				m_recv = leftstr;
			//Todo delete first empty line
		}
		m_recv_bytes += xmlstr_recv.size();
		xmlstr_recv.erase(std::remove(xmlstr_recv.begin(), xmlstr_recv.end(), '\r'),
				xmlstr_recv.end());
		xmlstr_recv.erase(std::remove(xmlstr_recv.begin(), xmlstr_recv.end(), '\n'),
				xmlstr_recv.end());

		switch(m_state)
		{
		case ADS_USER_CONNECTED:
			ret = action_on_subscribe(xmlstr_recv);
			break;
		case ADS_USER_SENT_SUB_RES:  // sent subscribe response
			ret = action_on_data_request(xmlstr_recv);
			break;
		case ADS_USER_RECVD_DATA_REQ: // received data request
			break;
		case ADS_USER_SEND_DATA_REQ_RES:  // sent data request response
		case ADS_USER_SEND_ASSIST_DATA: // sent assist data
			ret = action_on_unscribe_or_data_req(xmlstr_recv);
			break;
		default:
			break;
		}
		/*
		 * check ret value
		 * if error do action
		 */
		if(ret == -1)
		{
			clear_state();
			V(&m_act_lck);
			return ret;
		}
	}
	m_total_tick += GetTickCount() - start_tick;
	V(&m_act_lck);
	return ret;
}
/**
 * action_on_subscribe
 * 0 : success
 * -1: some error
 * 1)read xml from socket
 * 2)parser xml and the xml should have subscribe information
 * 3)parser xml and save username and pwd information
 * 4)check some basic parameter, return -1 if error: clear state and let the caller close the socket
 * 3)send subscribe response
 * 4)change m_state to next
 */
int CAdsUser::action_on_subscribe(const string& xmlrecvd)
{

	// parser xml subscribe
	unsub_subscribe_request sub_req;
	sub_req.reqSeqNum = "0";
	CXml_utils::do_parser_xml_un_subscribe_req(xmlrecvd, sub_req);
	syslog(LOG_INFO,"%s on fd %d, action %s, %s:%s\n",__PRETTY_FUNCTION__,m_fd,sub_req.action.c_str(),
			sub_req.user_id.c_str(),sub_req.user_pwd.c_str());

	m_req_number = str_convert<int>(sub_req.reqSeqNum);
	m_user_id = sub_req.user_id;
	m_user_pswd = sub_req.user_pwd;

	//check some basic parameter
	if(/*m_req_number < 0 ||*/
			m_user_id.size() <= 0||
			m_user_pswd.size() <= 0 ||
			sub_req.action != ADS_SUBSCRIBE_STR)
	{
		syslog(LOG_INFO,"ads_do_sub_req:parameter check error on fd %d\n", m_fd);
		return -1;// let the caller close the socket.
	}

	/**
	 * create subscribe res;
	 * only support GPS for test
	 */
	subscribe_response resp;
	resp.version = "2.0.0";
	resp.reqSeqNum = str_convert<string>(m_req_number);
	resp.sequenceNum = str_convert<string>(m_seq_number++);
	resp.supportedSystem_glonass = "NO";
	resp.supportedSystem_gps = "GPS";
	resp.error_code = "101";
	resp.error_reason = "only support GPS";
	resp.providerName = "superADP";
	string xmlstr_res = "";
	CXml_utils::do_create_xml_subscribe_res(xmlstr_res,resp);

	if(send_xml_data(xmlstr_res) == -1)
	{
		return -1;
	}
	m_state = ADS_USER_SENT_SUB_RES;
	return 0;
}


/**
 * 1) parser the data request in detail
 * 2) create data request response
 * 3) change to next state
 */

int CAdsUser::do_action_on_data_request(const data_request_gps& req_gps)
{

	//	m_req_number = atoi(req_gps.ReqSeqNum.c_str());
	m_req_number = str_convert<int>(req_gps.ReqSeqNum);
	//if possible check req number increased or not
	m_ref_l.ref_altitude = req_gps.refLocation.ref_altitude;
	m_ref_l.ref_latitude = req_gps.refLocation.ref_latitude;
	m_ref_l.ref_longitude = req_gps.refLocation.ref_longitude;
	//std::swap(m_services_list,req_gps.configList);
	list<config_data>::const_iterator ci;
	m_services_list.clear();
	memcpy(m_gps_srv_en_lst,default_gps_service_list,sizeof(default_gps_service_list));
	for(ci = req_gps.configList.begin(); ci!=req_gps.configList.end(); ci++)
	{
		/**
		 * set gps srv ennable list when data request changed
		 */
		for(int i = 0; i < MAX_GPS_SERVICE_NUMBER; i++)
		{
			if((*ci).dataName == m_gps_srv_en_lst[i].service_name)
			{
				m_gps_srv_en_lst[i].enable = true;
				break;
			}
		}

		ads_service_struct temp; // record the ads service name/type/interval
		clock_gettime(CLOCK_REALTIME, &temp.time_t);// save now time
		temp.data_sent = 0;
		temp.interval = 0;
		temp.type = ADS_SERVICE_UNSUPPORT;
		temp.service_name = (*ci).dataName;
		temp.type = data_type_convert_to_int((*ci).dataType);
		if((*ci).interval.size() == 0)
			temp.interval = 0;
		else
			temp.interval = str_convert<int>((*ci).interval);

		if(temp.interval < MIN_TIME_INTERVAL)
			temp.interval = MIN_TIME_INTERVAL;

		if(temp.type == ADS_SERVICE_ONCE || temp.type == ADS_SERVICE_UNSUPPORT)
		{
			temp.interval = 0;
		}
		m_services_list.push_back(temp);
	}

	//simple check whether request correct
	if(m_services_list.size() == 0)
	{
		syslog(LOG_INFO,"do_parser_xml_data_request_gps error on fd: %d\n", m_fd);
		return -1;
	}

	//Todo create xml data res
	data_response_gps responseData;
	string xmlstr_res = "";
	responseData.version  = "2.0.0";
	responseData.ReqSeqNum = str_convert<string>(m_req_number);
	responseData.sequenceNum = str_convert<string>(m_seq_number++);
	responseData.error_code = "0";
	responseData.error_reason = "No";

	/**
	 * create config list support/unsupport list using random function
	 */
	vector<ads_service_struct>::iterator si;
	for(si = m_services_list.begin(); si!=m_services_list.end(); si++)
	{
		config_data	temp;
		temp.dataName = (*si).service_name;
		//ads_service_type r_ads_types[] = {ADS_SERVICE_ONCE,ADS_SERVICE_ONCHANGED,\
		ADS_SERVICE_PERIOD,ADS_SERVICE_UNSUPPORT};
		//	unsigned int seed = 444;
		//(*si).type = r_ads_types[rand_r(&seed)%(ADS_SERVICE_PERIOD+1)];//random data
		data_type_convert_to_str((*si).type,temp.dataType);
		//temp.dataType = datatype;
		//temp.dataType = "Onchange";
		if(((*si).type == ADS_SERVICE_PERIOD) || ((*si).type == ADS_SERVICE_ONCHANGED
				||(*si).type == ADS_SERVICE_STOP))
		{
			if((*si).interval < MIN_TIME_INTERVAL)
			{
				temp.interval = str_convert<string>(MIN_TIME_INTERVAL);
			}
			else
			{
				temp.interval = str_convert<string>((*si).interval);
			}
		}
		responseData.configList.push_back(temp);
	}

	//responseData.configList.assign(m_services_list.begin(), m_services_list.end());
	CXml_utils::do_create_xml_data_res_gps(xmlstr_res, responseData);
	if(send_xml_data(xmlstr_res) == -1)
	{
		return -1;
	}
	// send out GPS Assist Data immediately
	m_state = ADS_USER_SEND_DATA_REQ_RES;
	return 0;
}
/*
 * action_on_data_request
 * 0: success\
 * -1: some failure
 * 1)read xml from socket
 * 2)parser xml and the xml should have data request information
 * 3)save request data
 * 4)create and send data req_response
 * 5)set state to next
 * 6)send data assist to client once
 */

int CAdsUser::action_on_data_request(const string& xmlrecvd)
{
	// parser xml data req
	data_request_gps req_gps;
	req_gps.ReqSeqNum = "0";
	CXml_utils::do_parser_xml_data_request_gps(xmlrecvd,req_gps);
	syslog(LOG_INFO,"%s on fd %d \n",__PRETTY_FUNCTION__,m_fd);

	if(-1 == do_action_on_data_request(req_gps))
		return -1;
	/**
	 * send GPS assist data one time
	 */
	/*
	if(send_file("../xml/GPS_AssistData.xml") == -1)
	{
		return -1;
	}
	 */
	m_state = ADS_USER_SEND_ASSIST_DATA;
	return 0;
}

/**
 * set assist data item in gps_assist_data;
 * name: service name
 * enable: true: enable the service; false:disable and free the memory
 */
void CAdsUser::set_assist_data_out(gps_assist_data &out_data, string name, bool enable)
{
#define do_delete_point_with_flag(point, flag) \
		if(!flag && point){			\
			delete point;\
			point = NULL;}

	if(name == ADS_SERVICE_ALMANAC)
	{
		do_delete_point_with_flag(out_data.almanacData,enable);
	}
	else if(name == ADS_SERVICE_UTCModel)
	{
		do_delete_point_with_flag(out_data.uTCModel, enable);
	}else if(name == ADS_SERVICE_IonosphericModel)
	{
		do_delete_point_with_flag(out_data.ionosphericModel, enable);
	}else if(name == ADS_SERVICE_NavigationModel )
	{
		do_delete_point_with_flag(out_data.navigationModel, enable);
	}else if(name == ADS_SERVICE_DGPSCorrections)
	{
		do_delete_point_with_flag(out_data.dGPSCorrections, enable);
	}else if(name == ADS_SERVICE_ReferenceTime)
	{
		do_delete_point_with_flag(out_data.referenceTime, enable);
	}else if(name == ADS_SERVICE_RealTimeIntegrity)
	{
		do_delete_point_with_flag(out_data.realTimeIntegrity, enable);
	}
}
/**
 * send_xml_assist_data
 * this function will be called every 10 sec
 * 0) get user_lock
 * 1) read a complete assist_data from file for test
 * 2) delete the service not in used, unsupport, stop
 * 3) send out the xml data
 * 5) free user_lock
 */

int CAdsUser::send_xml_assist_data()
{
	//remember P & V action!!!!!!!!!!!
	P(&m_act_lck);
	unsigned int start_tick = GetTickCount();
	int ret = 0;

	if(m_services_list.size() > 0 && m_state >= ADS_USER_SEND_DATA_REQ_RES)
	{
		gps_assist_data out_data;
		if(0 != do_read_assist_data(out_data))
		{
			V(&m_act_lck);
			syslog(LOG_INFO,"can't find an complete assist data sample file on fd %d\n", m_fd);
			return 0;
		}

		vector<ads_service_struct>::iterator si;
		for(si = m_services_list.begin(); si!= m_services_list.end(); si++)
		{

			if(ADS_SERVICE_UNSUPPORT != (*si).type )
			{// delete all unsupport service type
				struct timespec now_time;
				clock_gettime(CLOCK_REALTIME, &now_time);// get now time

				int delta = (now_time.tv_sec - ((*si).time_t.tv_sec+(*si).interval));
				srand((unsigned int)delta);
				int r = rand();
				//printf("name %s type %d, data_sent %d\n", (*si).service_name.c_str(),(*si).type,(*si).data_sent);
				//printf("now_time.tv_sec[%d] >=((*si).time_t.tv_sec[%d]+(*si).interval[%d], rand_r = %d, delta %d\n",
				//		now_time.tv_sec,(*si).time_t.tv_sec,(*si).interval, r,delta);

				if((*si).type == ADS_SERVICE_ONCE && (*si).data_sent == 0)
				{	//if first time
					set_assist_data_out(out_data,(*si).service_name,true);
					(*si).data_sent++;
				}else if((*si).type == ADS_SERVICE_PERIOD
						&& delta >= 0)
				{
					clock_gettime(CLOCK_REALTIME, &((*si).time_t));// update the time
					set_assist_data_out(out_data,(*si).service_name,true);
					(*si).data_sent++;
				}else if(((*si).type == ADS_SERVICE_ONCHANGED &&(r%2)) ||
						(delta >= 0 && (*si).type == ADS_SERVICE_ONCHANGED))
				{
					clock_gettime(CLOCK_REALTIME, &((*si).time_t));// update the time
					set_assist_data_out(out_data,(*si).service_name,true);
					(*si).data_sent++;
				}else
					set_assist_data_out(out_data,(*si).service_name,false);

			}else
			{
				set_assist_data_out(out_data,(*si).service_name,false);
			}

		}
		/**
		 * set assist data use gps service list
		 */
		for(int i = 0; i < MAX_GPS_SERVICE_NUMBER; i++)
		{

			if(!m_gps_srv_en_lst[i].enable)
			{
				set_assist_data_out(out_data,m_gps_srv_en_lst[i].service_name,false);
			}
		}

		//printf("out_data.almanacData [%d]&& out_data.dGPSCorrections[%d] out_data.ionosphericMode [%d] out_data.navigationModel[%d]\n"
		//		"out_data.uTCModel[%d] out_data.referenceTime[%d] out_data.realTimeIntegrity[%d]\n",out_data.almanacData ,out_data.dGPSCorrections,
		//		out_data.ionosphericModel,out_data.navigationModel,	out_data.uTCModel,out_data.referenceTime ,out_data.realTimeIntegrity);
		if(out_data.almanacData || out_data.dGPSCorrections
				|| out_data.ionosphericModel || out_data.navigationModel
				||out_data.uTCModel || out_data.referenceTime
				|| out_data.realTimeIntegrity
		)
		{
			string str_out;
			CXml_utils::do_create_xml_assist_data(str_out,out_data);
			if(send_xml_data(str_out) == -1)
			{
				clear_state();
				ret = -1;
			}

		}else{// if empty data, do not send assist data
			ret = 0;
		}

	}
	m_total_tick += GetTickCount() - start_tick;
	V(&m_act_lck);
	return ret;
}

/*
 * 1)read GPS_AssistData
 * 2)parser
 * 3)regenerate assistdata from configlist data
 *
 */
#define ASSIST_DATA_FILE "../xml/GPS_AssistData.xml"

/**
 * read an assist data file and do parser
 * gad_out: assist data struct
 */
int CAdsUser::do_read_assist_data(gps_assist_data& gad_out)
{
	struct stat sbuf;
	char *srcp;
	if (stat(ASSIST_DATA_FILE, &sbuf) < 0) {                     //line:netp:doit:beginnotfound
		printf("Forbidden Tiny couldn't read the file");
		return -1;
	}                                                    //line:netp:doit:endnotfound

	if (!(S_ISREG(sbuf.st_mode)) || !(S_IRUSR & sbuf.st_mode)) { //line:netp:doit:readable
		printf("Forbidden Tiny couldn't read the file");
		return -1;
	}
	int srcfd;
	/* Send response body to client */
	srcfd = Open(ASSIST_DATA_FILE, O_RDONLY, 0);    //line:netp:servestatic:open
	srcp = (char*) Mmap(0, sbuf.st_size, PROT_READ, MAP_PRIVATE, srcfd, 0);//line:netp:servestatic:mmap
	Close(srcfd);                           //line:netp:servestatic:close
	string ass_in_str = srcp;
	Munmap(srcp, sbuf.st_size);                 //line:netp:servestatic:munmap

	CXml_utils::do_parser_xml_assist_data(ass_in_str,gad_out);

	return 0;


}

/*
 * clear_state
 */
void CAdsUser::clear_state()
{
	syslog(LOG_INFO,"clear_state on  %d\n",m_fd);
	if(m_fd > 0)
		Close(m_fd);
	m_fd = -1;
	m_state =ADS_USER_INIT;
	m_seq_number = 1;
	m_req_number = 0;
	m_services_list.clear();
	m_ref_l.ref_altitude.clear();
	m_ref_l.ref_latitude.clear();
	m_ref_l.ref_longitude.clear();
	m_user_id.clear();
	m_user_pswd.clear();
	m_recv.clear();
	m_sent_bytes = 0;
	m_recv_bytes = 0;
	m_total_tick = 0;
	m_last_sent_tick = 0;
	m_last_recv_tick = 0;
	memcpy(m_gps_srv_en_lst,default_gps_service_list,sizeof(default_gps_service_list));
}



int CUserMgr::show_statistics(bool b_detail)
{
	vector_ads_type::iterator user_ptr;
	int online = 0;
	for(user_ptr = m_on_line_users.begin(); user_ptr!=m_on_line_users.end(); user_ptr++)
	{
		if((*user_ptr)->get_state() >= ADS_USER_CONNECTED)
		{
			online++;
			if(b_detail)
			{
				printf("fd %d at %s, send [%d] recv [%d] bytes, latest recv[%d], sent[%d], total tick[%d]\n",
						(*user_ptr)->get_fd(), USER_STATE_STR((*user_ptr)->get_state()),
						(*user_ptr)->get_send_bytes(), (*user_ptr)->get_recv_bytes(), (*user_ptr)->get_last_recv_tick(),
						(*user_ptr)->get_last_sent_tick(),(*user_ptr)->get_total_tick());
				fflush(stdout);
			}

		}
	}
	printf("online connection: %d\n\n", online);

}

/**
 *
 */
CUserMgr::CUserMgr(int max_users)
{
	Sem_init(&m_on_line_lck, 0, 1);      /* Binary semaphore for locking, every user will have one lock */
	for( int i = 0; i < max_users; i++)
	{
		ads_ptr temp(new CAdsUser(*this));
		m_on_line_users.push_back(temp);
	}
}
/**
 * trigger_test
 * used for one test thread
 * 1) loop every ads user
 * 2) find out the user which in valid state
 * 3) if the now time reach reach time out :create gps assist data
 * 4) random select some "Onchanged" user: add assist data
 * 5) update the time
 */
int CUserMgr::trigger_test()
{
	vector_ads_type::iterator user_ptr;
	for(user_ptr = m_on_line_users.begin(); user_ptr!=m_on_line_users.end(); user_ptr++)
	{

		// 1) check state
		// 2) get user lock
		// 3) update timer
		// 4) create xml data(time out item+random onchanged item)
		// 5) send out xml
		// 6) release user lock

	}
	return 0;
}
/**
 * if fd > m_on_line_users.size : new user, push to m_on_line_users,return m_on_line_users[fd];
 * if fd< m_on_line_user.size, return m_on_line_users[fd];
 */
ads_ptr CUserMgr::get_user(int fd){
	P(&m_on_line_lck);
	ads_ptr temp;
	if(fd >= m_on_line_users.size())
	{
		syslog(LOG_INFO,"%s new m_online_user on %d, m_on_line_user.size %d\n", __PRETTY_FUNCTION__,fd,m_on_line_users.size());

		int i = m_on_line_users.size();
		while(i <= fd)
		{
			ads_ptr temp(new CAdsUser(*this));
			m_on_line_users.push_back(temp);
			i++;
		}
	}
	V(&m_on_line_lck);
	return m_on_line_users[fd];
}
